package com.haois.mp.wechat.gateway;

import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import com.haois.mp.database.commom.enums.MpTypeEnum;
import com.haois.mp.database.module.common.daomain.UserToken;
import com.haois.mp.database.module.wxserver.service.BaseMessageService;
import com.haois.mp.database.module.common.service.IUserTokenService;
import com.haois.mp.database.util.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * WxGateway 微信转发网关
 */
@Controller
@RequestMapping("/gateway")
public class WxGateway {

    private static final Logger logger = LoggerFactory
            .getLogger(WxGateway.class);
    @Autowired
    private WxMpConfigStorage wxMpConfigStorage;
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private WxMpMessageRouter wxMpMessageRouter;
    @Autowired
    private BaseMessageService wxMsgService;
    @Autowired
    private IUserTokenService iUserTokenService;

    @Value("${spring.http.encoding.charset}")
    private String charset;

    /**
     * 微信公众号主网关，负责处理所有和微信公众号信息交互
     */
    @RequestMapping("/main")
    public void gateway(HttpServletRequest request, HttpServletResponse response, String xml)
            throws IOException {
        response.setCharacterEncoding(charset);
        logger.debug("接收到消息......");
        // 读取输入流
        Map<String, String> reqParam = getAllRequestParam(request);

        String signature = request.getParameter("signature");
        String nonce = request.getParameter("nonce");
        String timestamp = request.getParameter("timestamp");
        // 验签
        if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
            // 消息签名不正确，说明不是公众平台发过来的消息
            logger.info("非法请求");
            response.getWriter().println("非法请求");
            response.getWriter().close();
            return;
        }

        String echostr = request.getParameter("echostr");
        if (StringUtils.isNotBlank(echostr)) {
            // 说明是一个仅仅用来验证的请求，回显echostr
            logger.info("echostr:" + echostr);
            response.getWriter().println(echostr);
            response.getWriter().close();
            return;
        }

        String encryptType = request.getParameter("encrypt_type");
        // 加密类型
        encryptType = StringUtils.isBlank(encryptType) ? "raw" : encryptType;

        if ("raw".equals(encryptType)) {
            // 明文传输的消息
            String xmlStr = StringUtil.convertStreamToString(request.getInputStream());
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(xmlStr);
            logger.info("{}", xmlStr);
            wxMsgService.saveInMsg(inMessage);
            WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage);
            UserToken userToken = iUserTokenService.findByOpenId(inMessage.getFromUser(), MpTypeEnum.OFFACCOUNTS.getType());
            if(userToken != null){
                if (outMessage != null) {
                    long msgId = 0;
                    if (null != inMessage.getMsgId()) {
                        msgId = inMessage.getMsgId();
                    }
                    wxMsgService.saveOutMsg(outMessage, msgId);
                    // 说明是同步回复的消息
                    // 将xml写入HttpServletResponse
                    response.getWriter().write(outMessage.toXml());
                } else {
                    // 说明是异步回复的消息，直接将空字符串写入HttpServletResponse
                    response.getWriter().write("");
                }
            }else {
                //String reply = "<a href=\"info/login\">" + "请先绑定账号" + "</a>";
                response.getWriter().write(outMessage.toXml());
            }

            response.getWriter().close();
            return;
        }

        if ("aes".equals(encryptType)) {
            // 是aes加密的消息
            String msgSignature = request.getParameter("msg_signature");
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(
                    request.getInputStream(), wxMpConfigStorage, timestamp,
                    nonce, msgSignature);
            WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage);
            response.getWriter().write(
                    outMessage.toEncryptedXml(wxMpConfigStorage));
            /**
             * 微信服务器发送三次重复的排重问题解决流程：
             a.得到数据response.getWriter();
             b.得到数据request下的所有数据--->写入一个HashMap中
             c.使用response的writer返回一个空白，并且关闭writer，注意如果不关闭的话，那么这个空白消息是不会被传给微信服务器的
             */
            response.getWriter().close();
            return;
        }

        response.setContentType("text/html;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        response.getWriter().println("不可识别的加密类型");
        response.getWriter().close();
        return;
    }


    /**
     * 获取请求参数中所有的信息
     *
     * @param request
     * @return
     */
    public static Map<String, String> getAllRequestParam(final HttpServletRequest request) {
        Map<String, String> res = new HashMap<String, String>();
        Enumeration<?> temp = request.getParameterNames();
        if (null != temp) {
            while (temp.hasMoreElements()) {
                String en = (String) temp.nextElement();
                String value = request.getParameter(en);
                res.put(en, value);
                //logger.debug("en:"+en + ">>>>>>" + "value:" + value);
                if (null == res.get(en) || "".equals(res.get(en))) {
                    res.remove(en);
                }
            }
        }
        return res;
    }

}
